package server

import (
	"context"
	"errors"
	"fmt"
	"longmen/server/config/global"
	"longmen/server/internal/rpc/tencentim/imsdk"
	"longmen/server/internal/services/nami/services/sports/basketball/bbcache"
	_const "longmen/server/pkg/common/helper/const"
	time2 "longmen/server/pkg/common/helper/timehelper"
	"longmen/server/pkg/common/helper/util"
	"longmen/server/pkg/db/mysql/db_live"
	"longmen/server/pkg/db/mysql/db_sports"
	db_sports2 "longmen/server/pkg/db/mysql/models/models_sports"
	"longmen/server/pkg/db/redis/cache"
	"longmen/server/pkg/protobuf"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/jinzhu/copier"

	"longmen/server/pkg/common/api_models"

	"github.com/labstack/echo/v4"
	"github.com/spf113/cast"
)

var lock sync.Mutex
var scoreLock sync.Mutex

/**
* @Author:admin
* @Description: 首页推荐赛事列表
* @Param :
* @Return :
* @Date: 18:08 2022/7/1
 */
func (s *Server) GetRecommendMatches4Crawler(ctx context.Context, req *protobuf.GetRecommendMatches4CrawlerReq) (*protobuf.GetRecommendMatches4CrawlerResp, error) {

	cacheKey := recommendMatcheCacheKey(cast.ToUint32(req.GetSportId()), req.Date)
	resp := new(protobuf.GetRecommendMatches4CrawlerResp)
	//先从缓存中取,如果缓存中没有，从数据库里面取
	err := getRedisCache(&resp, cacheKey)
	if err != nil {
		var matchIds []int
		compareDateAndNow := time2.CompareDateAndNow(req.Date)
		//startTime, endTime := util.INaDay2Unix(util.EMPTY_STR)
		startTime, endTime := time2.INaDay2Unix(req.Date)
		//params["match_time"] = map[string]interface{}{">=": startTime, "<=": endTime}
		matchTemps, err := db_sports.GetRecommendMatches4Crawler(startTime, endTime, req.SportId, compareDateAndNow)

		if err != nil {
			return nil, fmt.Errorf("%s%s", util.Convert2(), err.Error())
		}
		for _, matchTemp := range matchTemps {
			if matchTemp.MatchStatus == 1 {
				matchIds = append(matchIds, matchTemp.MatchId)
			}
		}
		var lives []*protobuf.LiveInfo
		total := 0
		if len(matchIds) > 0 {
			lives, _ = db_live.GetLivePbParamForIds(matchIds)
		}
		for _, v := range matchTemps {
			tempLives := make([]*protobuf.LiveInfo, 0)
			if v.MatchId > 0 && v.MatchStatus == 1 {
				for _, l := range lives {
					if v.MatchId == int(l.Matchid) {
						tempLives = append(tempLives, l)
					}
				}
			}
			if global.Lang.GetLang() != _const.LangChinese {
				v.Away = v.AwayEn
				v.Home = v.HomeEn
				v.Comp = v.CompShortEn
				v.MatchDetailStatus = util.GetMatchStatusCodeEn(v.SportId, v.RawMatchStatus)
			}
			matchesSuggest := &protobuf.MatchesSuggest{
				Away:              v.Away,
				AwayScore:         v.AwayScore,
				AwayLog:           v.AwayLogo,
				Comp:              v.Comp,
				Home:              v.Home,
				HomeScore:         v.HomeScore,
				HomeLog:           v.HomeLogo,
				CompLog:           v.CompLogo,
				MatchDetailStatus: v.MatchDetailStatus,
				MatchTime:         v.MatchTime,
				TimePlayerd:       v.TimePlayed,
				Lives:             tempLives,
				SportId:           uint32(v.SportId),
				MatchId:           uint32(v.MatchId),
				RawMatchStatus:    int32(v.RawMatchStatus),
				CompShortZh:       v.CompShortZh,
			}
			if v.MatchStatus == 1 {
				resp.Staring = append(resp.Staring, matchesSuggest)
				total++
			}
			if v.MatchStatus == 2 {
				resp.Pending = append(resp.Pending, matchesSuggest)
				total++
			}
			if v.MatchStatus == 3 {
				resp.Finished = append(resp.Finished, matchesSuggest)
				total++
			}
		}
		resp.Total = int32(len(matchTemps))
		setRedisCache(resp, cacheKey, _const.SPORT_MATCH_INDEX_EXPIRY_KEY)
	}
	//开赛状态的联赛比分从缓存中取
	if len(resp.Staring) > 0 {
		ballCacheScore(resp.Staring)
	}
	return resp, nil
}

/**
* @Author:admin
* @Description: 此处key 必须加入日期当到了12点过，如果没有数据直接从数据库取，就不会产生脏数据
* @Param :
* @Return :
* @Date: 15:39 2022/6/24
 */
func recommendMatcheCacheKey(sportId uint32, date string) string {
	//day := timehelper.Now().Format(util.TIME_LAYOUT_FORMAT_3)
	return fmt.Sprintf("%s_%s_%d", _const.SPORT_MATCH_INDEX_KEY, date, sportId)
}

/**
* @Author:admin
* @Description: 首页中热门赛事
* @Param :
* @Return :
* @Date: 14:48 2022/6/22
 */
func (s *Server) GetHotMatchCards4Crawler(ctx context.Context, req *protobuf.GetHotMatchCards4CrawlerReq) (*protobuf.GetHotMatchCards4CrawlerResp, error) {
	list := make([]*protobuf.GetHotMatchCards4Crawler, 0)
	//先从缓存中取数据
	cacheKey := hotMatchCardCacheKey()
	err := getRedisCache(&list, cacheKey)
	if err != nil {
		//从缓存中或数据库中取数据
		list, err = getHotMatchCardsFromRedisOrDb(cacheKey)
		if err != nil {
			return nil, fmt.Errorf("%s%s", util.Convert2(), err.Error())
		}
		//从redis 缓存中查找比分
		if len(list) > 0 {
			ballCacheScore(list)
		}
	}
	if req.GetUserId() > 0 {
		matchTempSchedules, err := db_sports.GetReservedMatch4Crawler(int(req.GetUserId()))
		if err == nil {
			for _, match := range matchTempSchedules {
				for _, v := range list {
					if int(v.MatchId) == match.MatchId {
						v.Favorite = 1
					}
				}
			}

		}
	}
	return &protobuf.GetHotMatchCards4CrawlerResp{
		GetHotMatchCards4Crawler: list,
	}, nil
}

/**
* @Author:admin
* @Description: 从redis 缓存中查找比分首页热门赛事
* @Param :
* @Return :
* @Date: 14:10 2022/6/29
 */

func ballCacheScore(list interface{}) {

	var footBallMatchIds []string
	var basketBallMatchIds []string
	values := reflect.ValueOf(list)
	for i := 0; i < values.Len(); i++ {
		elem := values.Index(i).Elem()
		matchId := elem.FieldByName(_const.BALL_CACHE_MATCHID).Uint()
		switch elem.FieldByName(_const.BALL_CACHE_SPORTID).Uint() {
		case _const.FOOT_BALL_SPORT_ID:
			footBallMatchIds = append(footBallMatchIds, fmt.Sprintf(_const.MATCH_FOOTBALL_SCORE_CACHE_PRE+"%s", cast.ToString(matchId)))
		case _const.BASKET_BALL_SPORT_ID:
			basketBallMatchIds = append(basketBallMatchIds, fmt.Sprintf(_const.MATCH_BASKETBALL_SCORE_CACHE_PRE+"%s", cast.ToString(matchId)))
		}
	}
	footBallRedisCache := getFootBallBatchRedisCache(footBallMatchIds)
	basketBallRedisCache := getBasketBallBatchRedisCache(basketBallMatchIds)
	//从map 中找数据
	for i := 0; i < values.Len(); i++ {
		elem := values.Index(i).Elem()
		matchId := elem.FieldByName(_const.BALL_CACHE_MATCHID).Uint()
		//找足球的比分
		if value, ok := footBallRedisCache[int64(matchId)]; ok {
			values.Index(i).Elem().FieldByName(_const.BALL_CACHE_AWAYSCORE).Set(reflect.ValueOf(value.AwayScore.Score))
			values.Index(i).Elem().FieldByName(_const.BALL_CACHE_HOMESCORE).Set(reflect.ValueOf(value.AwayScore.Score))
			//比赛状态 暂时未有写入
			//val.MatchStatus = uint32(value.MatchStatus)
		}
		//找篮球的比分
		if value, ok := basketBallRedisCache[int64(matchId)]; ok {
			values.Index(i).Elem().FieldByName(_const.BALL_CACHE_AWAYSCORE).Set(reflect.ValueOf(value.AwayScore.Score))
			values.Index(i).Elem().FieldByName(_const.BALL_CACHE_HOMESCORE).Set(reflect.ValueOf(value.AwayScore.Score))
			//比赛状态 暂时未有写入
			//val.MatchStatus = uint32(value.MatchStatus)
		}
	}
}

/**
* @Author:admin
* @Description: 此处key 必须加入日期当到了12点过，如果没有数据直接从数据库取，就不会产生脏数据
* @Param :
* @Return :
* @Date: 15:39 2022/6/24
 */
func hotMatchCardCacheKey() string {
	day := time.Now().Format(time2.TIME_LAYOUT_FORMAT_3)
	return fmt.Sprintf("%s_%s", _const.SPORT_HOT_INDEX_KEY, day)
}

/**
* @Author:admin
* @Description: 从redis中或db中取数据
* @Param :
* @Return :
* @Date: 10:16 2022/6/22
 */

func getHotMatchCardsFromRedisOrDb(cacheKey string) ([]*protobuf.GetHotMatchCards4Crawler, error) {
	list := make([]*protobuf.GetHotMatchCards4Crawler, 0)
	err := getRedisCache(&list, cacheKey)
	if err != nil {
		lock.Lock()
		defer lock.Unlock()
		var matchIds []interface{}
		startTime, endTime := time2.INaDay2Unix(_const.EMPTY_STR)
		resp, err := db_sports.GetHotMatchCards4Crawler(startTime, endTime)
		if err != nil {
			return nil, err
		}
		for _, match := range resp {
			matchIds = append(matchIds, int(match.MatchId))
		}
		list = make([]*protobuf.GetHotMatchCards4Crawler, 0)
		if len(resp) > 0 {
			lives := db_sports.GetMatchLive(matchIds, 5)
			toMap := liveListToMap(lives)
			for _, match := range resp {
				tempRs := &protobuf.GetHotMatchCards4Crawler{}
				copier.Copy(&tempRs, &match)
				if val, ok := toMap[match.MatchId]; ok {
					tempRs.AnchorAvatars = val.AnchorAvatars
					tempRs.AnchorIds = val.AnchorIds
				}
				if global.Lang.GetLang() == _const.LangChinese {
					tempRs.Away = match.Away
					tempRs.Home = match.Home
					tempRs.Comp = match.Comp
				} else {
					tempRs.Away = match.AwayEn
					tempRs.Home = match.HomeEn
					tempRs.Comp = match.CompShortEn
					tempRs.MatchDetailStatus = util.GetMatchStatusCodeEn(match.SportId, match.RawMatchStatus)
				}
				list = append(list, tempRs)
			}
		}
		setRedisCache(list, cacheKey, _const.SPORT_HOT_INDEX_EXPIRY_KEY)
	}
	return list, nil
}

/**
 * @Author admin
 * @Description //取赛事联赛
 * @version: v1.8.0
 * @Date 9:26 2022/7/13
 * @Param
 * @return
 **/
func (s *Server) SportLiveMatch(ctx context.Context, req *protobuf.SportLiveMatchReq) (*protobuf.SportLiveMatchResp, error) {
	match, err := db_sports.SportLiveMatch(uint8(req.SportId), req.Comp)
	if err != nil {
		return nil, err
	}
	return match, nil
}

/**
 * @Author admin
 * @Description //取赛事联赛列表
 * @version: v1.8.0
 * @Date 9:25 2022/7/13
 * @Param
 * @return
 **/
func (s *Server) SportLiveMatchComp(ctx context.Context, req *protobuf.SportLiveMatchReq) (*protobuf.SportLiveMatchCompResp, error) {
	match, err := db_sports.SportLiveMatchComp(cast.ToUint8(req.SportId))
	if err != nil {
		return nil, err
	}
	return match, nil
}

/**
* @Author:admin
* @Description: 职赛别名
* @Param :
* @Return :
* @Date: 18:35 2022/6/21
 */
func (s *SportServer) GetCompetation4Crawler(ctx context.Context, req *protobuf.GetCompetation4CrawlerReq) (*protobuf.GetCompetation4CrawlerResp, error) {
	ret := &protobuf.GetCompetation4CrawlerResp{}
	compList := make([]*protobuf.GetCompetation4CrawlerItem, 0)
	//从redis 中取出数据
	cacheKey := generateCacheKey(_const.SPORT_MENU_COMP_KEY, req.Type)
	err := getRedisCache(&compList, cacheKey)
	if err != nil {
		compList, _ = getCompFromRedisOrDb(compList, req.Type)
	}
	ret.CompList = compList
	return ret, nil
}

/**
* @Author:admin
* @Description: 从redis中或db中取数据
* @Param :
* @Return :
* @Date: 10:16 2022/6/22
 */
func getCompFromRedisOrDb(compList []*protobuf.GetCompetation4CrawlerItem, type1 int32) ([]*protobuf.GetCompetation4CrawlerItem, error) {
	lock.Lock()
	defer lock.Unlock()
	//类似于单例模式中的双检
	cacheKey := generateCacheKey(_const.SPORT_MENU_COMP_KEY, type1)
	err := getRedisCache(&compList, cacheKey)
	if err != nil {
		compList, err = db_sports.GetCompetation4Crawler(uint8(type1))
		if err != nil {
			return nil, fmt.Errorf("%s%s", util.Convert2(), err)
		}
		compList = append([]*protobuf.GetCompetation4CrawlerItem{
			//{Comp: util.SPORT_MENU_TITLE_ALL, CompPic: util.SPORT_MENU_TITLE_ALL_PIC},
			{Comp: _const.SPORT_MENU_TITLE_HOT, CompPic: _const.SPORT_MENU_TITLE_HOT_PIC}}, compList...)
		cacheKey := generateCacheKey(_const.SPORT_MENU_COMP_KEY, type1)
		setRedisCache(compList, cacheKey, _const.SPORT_MENU_COMP_EXPIRY_KEY)
	}
	return compList, nil
}

/**
* @Author:admin
* @Description: 为赛事列表
* @Param :
* @Return :
* @Date: 13:15 2022/6/22
 */
func getCompFromCache(comp string) string {
	cacheKey := generateCacheKey(_const.SPORT_MENU_COMP_FOR_MATCH_KEY, 0)
	compList := make([]*db_sports2.CompLabel, 0)
	err := getRedisCache(&compList, cacheKey)
	if err != nil {
		lock.Lock()
		defer lock.Unlock()
		//类似于单例模式中的双检
		err = getRedisCache(&compList, cacheKey)
		if err != nil {
			compList, err = db_sports.GetCompLabelList()
			if err != nil {
				return _const.EMPTY_STR
			}
			setRedisCache(compList, cacheKey, _const.SPORT_MENU_COMP_EXPIRY_KEY)
		}
	}
	//从list 中取出与标签相同的联赛名称
	for _, val := range compList {
		if comp == val.Label {
			return val.Comp
		}
	}
	return _const.EMPTY_STR
}

func (s *SportServer) GetMatch4Crawler(ctx context.Context, req *protobuf.GetMatch4CrawlerReq) (*protobuf.GetMatch4CrawlerResp, error) {
	fName := "GetMatch4Crawler"
	param := echo.Map{}
	var matchIds []int    //比赛ID
	var myMatchIds []uint //预约的比赛ID
	var flag bool
	anchorReservationList := make([]*api_models.GetAnchorReservation, 0)
	//改变查询条件
	if req.Competation != _const.EMPTY_STR {
		//是热门
		if req.Competation == _const.SPORT_MENU_TITLE_HOT {
			param["flag"] = 2
			//conditionSql=fmt.Sprintf("%s tb1.flag=%d and",conditionSql,2)
			//不是所有
		} else if req.Competation != _const.SPORT_MENU_TITLE_ALL {
			param["comp"] = getCompFromCache(req.Competation)
			//conditionSql=fmt.Sprintf("%s tb1.comp='%s' and",conditionSql,getCompFromCache(req.Competation))
		}
	}
	if req.SportId != 0 {
		param["sport_id"] = req.SportId
		//conditionSql=fmt.Sprintf("%s tb1.sport_id=%d and",conditionSql,req.SportId)
	}
	if req.MatchStatus >= 1 && req.MatchStatus <= 3 {
		param["match_status"] = req.MatchStatus
		//conditionSql=fmt.Sprintf("%s tb1.match_status=%d and",conditionSql,req.MatchStatus)
	}
	if req.Date != "" {
		begin, end := time2.INaDay2Unix(req.Date)
		param["match_time"] = map[string]interface{}{">=": begin, "<=": end}
	}
	if req.IsFlag == 2 {
		param["flag"] = 2
	}

	match4Crawler, count, soccerCount, basketballCount, err := db_sports.GetMatch4Crawler(param, req.IsLive, int(req.Page), int(req.Size))
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	for _, matchTemp := range match4Crawler {
		matchIds = append(matchIds, matchTemp.MatchId)
	}
	if len(matchIds) > 0 {
		anchorReservationList, err = db_sports.GetAnchorReservationNew(matchIds)
		if err != nil {
			return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
		}
	}
	if req.Uid > 0 {
		myMatchIds, err = db_sports.GetReservedMatchIds(uint(req.Uid))
		if err != nil {
			return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
		}
		flag = true
	}
	lives, err := db_live.GetLivePbParam(map[string]interface{}{"status": 1, "matchid": map[string]interface{}{">": 1}}, "hot desc", 0, 0)
	matchList := make([]*protobuf.Match4Crawler, 0)
	for _, matchTemp := range match4Crawler {
		reservation := make([]*protobuf.Reservation, 0)
		var isReserved bool
		for _, anchor := range anchorReservationList {
			if anchor.MatchId == uint(matchTemp.MatchId) {
				reservation = append(reservation, &protobuf.Reservation{
					AnchorId:     uint64(anchor.AnchorId),
					AnchorAvatar: anchor.AnchorAvatar,
				})
			}
		}
		for _, live := range lives {
			if int(live.Matchid) == matchTemp.MatchId {
				reservation = append(reservation, &protobuf.Reservation{
					AnchorId:     uint64(live.Anchorid),
					AnchorAvatar: live.AnchorAvatar,
				})
			}
		}
		if flag {
			for _, matchid := range myMatchIds {
				if int(matchid) == matchTemp.MatchId {
					isReserved = true
				}
			}
		}
		/*if matchTemp.HomeLogo == "" {
			matchTemp.HomeLogo = cache.GetConfigPub().DefaultMatchLogoUrl
		}
		if matchTemp.AwayLogo == "" {
			matchTemp.AwayLogo = cache.GetConfigPub().DefaultMatchLogoUrl
		}*/
		matchList = append(
			matchList,
			&protobuf.Match4Crawler{
				MatchId:         int32(matchTemp.MatchId),
				MatchEventTitle: matchTemp.Comp,
				MatchTime:       matchTemp.MatchTime,
				HomeTeamName:    matchTemp.Home,
				HomeTeamLogo:    matchTemp.HomeLogo,
				HomeScore:       int32(matchTemp.HomeScore),
				AwayTeamName:    matchTemp.Away,
				AwayTeamLogo:    matchTemp.AwayLogo,
				AwayScore:       int32(matchTemp.AwayScore),
				MatchStatus:     int32(matchTemp.MatchStatus),
				IsReserved:      isReserved,
				Weights:         int32(util.GetMatchWeights(matchTemp.Comp)),
				SportId:         int32(uint8(matchTemp.SportId)),
				Reservation:     reservation,
			},
		)
	}
	//从缓存中取比分
	if len(matchList) > 0 {
		matchBallCacheScore(matchList)
	}

	return &protobuf.GetMatch4CrawlerResp{
		Count:           count,
		SoccerCount:     soccerCount,
		BasketballCount: basketballCount,
		Match:           matchList,
	}, nil
}

func matchBallCacheScore(list []*protobuf.Match4Crawler) {
	var footBallMatchIds []string
	var basketBallMatchIds []string
	for _, val := range list {
		switch val.SportId {
		case _const.FOOT_BALL_SPORT_ID:
			footBallMatchIds = append(footBallMatchIds, fmt.Sprintf(_const.MATCH_FOOTBALL_SCORE_CACHE_PRE+"%s", cast.ToString(val.MatchId)))
		case _const.BASKET_BALL_SPORT_ID:
			basketBallMatchIds = append(basketBallMatchIds, fmt.Sprintf(_const.MATCH_BASKETBALL_SCORE_CACHE_PRE+"%s", cast.ToString(val.MatchId)))
		}
	}
	footBallRedisCache := getFootBallBatchRedisCache(footBallMatchIds)
	basketBallRedisCache := getBasketBallBatchRedisCache(basketBallMatchIds)
	//从map 中找数据
	for _, val := range list {
		//找足球的比分
		if value, ok := footBallRedisCache[int64(val.MatchId)]; ok {
			val.AwayScore = int32(value.AwayScore.Score)
			val.HomeScore = int32(value.HomeScore.Score)
			//比赛状态 暂时未有写入
			//val.MatchStatus = uint32(value.MatchStatus)
		}
		//找篮球的比分
		if value, ok := basketBallRedisCache[int64(val.MatchId)]; ok {
			val.AwayScore = int32(value.AwayScore.Score)
			val.HomeScore = int32(value.HomeScore.Score)
			//比赛状态 暂时未有写入
			//val.MatchStatus = uint32(value.MatchStatus)
		}
	}
}

/**
* @Author:admin
* @Description:预约赛事列表
* @Param :
* @Return :
* @Date: 13:09 2022/6/23
 */

func (s *SportServer) GetReservedMatch4Crawler(ctx context.Context, getReservedReq *protobuf.GetReservedMatch4CrawlerReq) (*protobuf.GetReservedMatch4CrawlerResp, error) {
	var matchIds []int
	anchorReservationList := make([]*api_models.GetAnchorReservation, 0)
	resp := make([]*protobuf.GetReservedMatch4CrawlerItem, 0)
	//查询预约赛事
	matchList, err := db_sports.GetReservedMatch4Crawler(int(getReservedReq.UserId))
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert2(), err.Error())
	}
	//收集赛事ID
	for _, match := range matchList {
		matchIds = append(matchIds, match.MatchId)
	}
	if len(matchIds) > 0 {
		//查询赛事
		matchList, _ = db_sports.GetMatchTempScheduleByIds(matchIds)
		//根据赛事ID
		anchorReservationList, err = db_sports.GetAnchorReservationNew(matchIds)
		if err != nil {
			return nil, fmt.Errorf("%s%s", util.Convert2(), err.Error())
		}
		reservedMatch := make([]*protobuf.Match4Crawler, 0)
		for _, matchTemp := range matchList {
			//产生一个新预约赛事记录(球队信息，主播信息)
			reservedMatch = produceReservedMatch(reservedMatch, anchorReservationList, matchTemp)
		}

		//按日期group by 分组
		currentTime := time2.Stamp2Time(time.Now().Unix()).Format(time2.TIME_LAYOUT_FORMAT_4) //记录当前时间
		respTemp := make([]*protobuf.GetReservedMatch4CrawlerItem, 0)
		for _, match := range reservedMatch {
			// 如果有被收集到就循环下一次
			if collectReservedMatch(resp, match) {
				continue
			}
			if collectReservedMatch(respTemp, match) {
				continue
			}
			//把以上未有收集到的日期 按日期分组，新增一条日期并加入到收集器(resp或respTemp)中
			resp, respTemp = groupingReservedMatch(resp, respTemp, match, currentTime)
		}
		//合并
		resp = append(resp, respTemp...)
	}
	return &protobuf.GetReservedMatch4CrawlerResp{Item: resp}, nil
}

/**
* @Author:admin
* @Description: 按日期分组
* @Param :
* @Return :
* @Date: 10:55 2022/7/6
 */

func groupingReservedMatch(resp []*protobuf.GetReservedMatch4CrawlerItem, respTemp []*protobuf.GetReservedMatch4CrawlerItem, match *protobuf.Match4Crawler, currentTime string) ([]*protobuf.GetReservedMatch4CrawlerItem, []*protobuf.GetReservedMatch4CrawlerItem) {
	matchResp := &protobuf.GetReservedMatch4CrawlerItem{}
	matchResp.Date = time2.Stamp2Time(match.MatchTime).Format(time2.TIME_LAYOUT_FORMAT_4)
	matchResp.Match = append(matchResp.Match, match)
	//顺序为按日期 今天，未开赛升序，已开赛升序
	//小于今天
	if strings.Compare(currentTime, matchResp.Date) > 0 {
		respTemp = sortMatchTime(respTemp, matchResp)
	} else {
		//大于等于今天
		resp = sortMatchTime(resp, matchResp)
	}
	return resp, respTemp
}

/**
* @Author:admin
* @Description:按日期排序，并收集
* @Param :
* @Return :
* @Date: 10:53 2022/7/6
 */
func sortMatchTime(resp []*protobuf.GetReservedMatch4CrawlerItem, matchResp *protobuf.GetReservedMatch4CrawlerItem) []*protobuf.GetReservedMatch4CrawlerItem {
	//循环收集器中的赛事
	for i, val := range resp {
		//把当前日期插入有序列表中
		if strings.Compare(val.Date, matchResp.Date) >= 0 {
			return append(resp[:i], append([]*protobuf.GetReservedMatch4CrawlerItem{matchResp}, resp[i:]...)...)
		}
	}
	//日期是从小到大 从未有插入，加入到队尾
	//第1次是resp为空，直接加入到respTemp 队尾
	return append(resp, matchResp)
}

/**
* @Author:admin
* @Description: 按日期收集赛事信息到同一个队列
* @Param :
* @Return :
* @Date: 10:25 2022/7/6
 */
func collectReservedMatch(resp []*protobuf.GetReservedMatch4CrawlerItem, match *protobuf.Match4Crawler) bool {
	for _, warp := range resp {
		if time2.Stamp2Time(match.MatchTime).Format(time2.TIME_LAYOUT_FORMAT_4) == warp.Date {
			warp.Match = append(warp.Match, match)
			return true
		}
	}
	return false
}

/**
* @Author:admin
* @Description: 设置球队默认值
* @Param :
* @Return :
* @Date: 10:02 2022/7/6
 */
func setDefaultLog(matchTemp *db_sports2.MatchTempSchedule) {
	//设置默认值
	if matchTemp.HomeLogo == _const.EMPTY_STR {
		matchTemp.HomeLogo = cache.GetConfigPub().DefaultMatchLogoUrl
	}
	if matchTemp.AwayLogo == _const.EMPTY_STR {
		matchTemp.AwayLogo = cache.GetConfigPub().DefaultMatchLogoUrl
	}
}

/**
* @Author:admin
* @Description: 为每一赛事找到主最多5名主播
* @Param :
* @Return :
* @Date: 10:02 2022/7/6
 */
func produceReservation(anchorReservationList []*api_models.GetAnchorReservation, MatchId uint) []*protobuf.Reservation {
	reservation := make([]*protobuf.Reservation, 0)
	for _, anchor := range anchorReservationList {
		if anchor.MatchId == MatchId {
			reservation = append(reservation, &protobuf.Reservation{
				AnchorId:     uint64(anchor.AnchorId),
				AnchorAvatar: anchor.AnchorAvatar,
			})
		}
	}
	return reservation
}

/**
* @Author:admin
* @Description: 产生一个新的type
* @Param :
* @Return :
* @Date: 10:05 2022/7/6
 */
func produceReservedMatch(reservedMatch []*protobuf.Match4Crawler, anchorReservationList []*api_models.GetAnchorReservation, matchTemp *db_sports2.MatchTempSchedule) []*protobuf.Match4Crawler {
	//设置不球队默认logo
	//setDefaultLog(matchTemp)

	reservedMatch = append(reservedMatch, &protobuf.Match4Crawler{
		MatchId:         int32(matchTemp.MatchId),
		MatchEventTitle: matchTemp.Comp,
		MatchTime:       matchTemp.MatchTime,
		HomeTeamName:    matchTemp.Home,
		HomeTeamLogo:    matchTemp.HomeLogo,
		HomeScore:       int32(matchTemp.HomeScore),
		AwayTeamName:    matchTemp.Away,
		AwayTeamLogo:    matchTemp.AwayLogo,
		AwayScore:       int32(matchTemp.AwayScore),
		MatchStatus:     int32(matchTemp.MatchStatus),
		IsReserved:      true,
		SportId:         int32(matchTemp.SportId),
		Reservation:     produceReservation(anchorReservationList, uint(matchTemp.MatchId)),
	})
	return reservedMatch
}

/**
* @Author:admin
* @Description:预约赛事
* @Param :
* @Return :
* @Date: 13:07 2022/6/23
 */
func (s *SportServer) ReserveMatch4Crawler(ctx context.Context, ReserveReq *protobuf.ReserveMatch4CrawlerReq) (*protobuf.ReserveMatch4CrawlerResp, error) {
	fName := "ReserveMatch4Crawler"
	match, err := db_sports.GetMatch4CrawlerByParam(echo.Map{"match_id": ReserveReq.MatchId, "match_status": 2}, "", "", 0, 0) // //比赛状态, 1-进行,2-未开赛,3-完场先查找是否有此场赛事
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	if len(match) < 1 {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), "找不到此场赛事或赛事已经开始!")
	}
	ok, err := db_sports.InsertUserMatchReservation(int(ReserveReq.MatchId), int(ReserveReq.UserId), int(ReserveReq.IsAnchor), int(ReserveReq.LiveTime))
	if err != nil {
		return nil, fmt.Errorf("%s%s", util.Convert(fName), err.Error())
	}
	return &protobuf.ReserveMatch4CrawlerResp{
		IsSuccess: ok,
	}, nil
}

/**
* @Author:admin
* @Description:取消预约赛事
* @Param :
* @Return :
* @Date: 13:08 2022/6/23
 */
func (s *SportServer) CancelReserveMatch4Crawler(ctx context.Context, ReserveReq *protobuf.ReserveMatch4CrawlerReq) (*protobuf.ReserveMatch4CrawlerResp, error) {
	ok := db_sports.DeleteUserMatchReservation(int(ReserveReq.MatchId), int(ReserveReq.UserId))
	return &protobuf.ReserveMatch4CrawlerResp{
		IsSuccess: ok,
	}, nil
}

/**
* @Author:admin
* @Description:从redis 取一个联赛菜单列表
* @Param :
* @Return :
* @Date: 9:46 2022/6/22
 */
func getRedisCache(compList interface{}, key string) error {

	data, err := global.GetRds().Get(key).Result()
	if err != nil {
		return fmt.Errorf("%s%s", util.Convert2(), err.Error())
	}
	err = util.Json2Struct(data, &compList)
	if err != nil {
		fmt.Println(err)
		return fmt.Errorf("%s%s", util.Convert2(), err.Error())
	}
	return nil
}

/**
* @Author:admin
* @Description: 从redis 中批量获取数据 足球与蓝球分别获取
* @Param :
* @Return :
* @Date: 11:15 2022/6/29
 */
func getFootBallBatchRedisCache(matchIds []string) map[int64]*db_sports2.FootBallMatchRedis {
	ballMatchRedisMap := make(map[int64]*db_sports2.FootBallMatchRedis, 0)
	result, _ := global.GetRds().MGet(matchIds...).Result()
	for _, val := range result {
		match := &db_sports2.FootBallMatchRedis{}
		if v, ok := val.(string); ok {
			if util.Json2Struct(strings.TrimSuffix(strings.TrimPrefix(v, "["), "]"), &match) == nil {
				ballMatchRedisMap[match.MatchId] = match
			}
		}
	}
	return ballMatchRedisMap
}

/**
* @Author:admin
* @Description: 从redis 中批量获取数据 蓝球分别获取
* @Param :
* @Return :
* @Date: 11:15 2022/6/29
 */

func getBasketBallBatchRedisCache(matchIds []string) map[int64]*db_sports2.FootBallMatchRedis {
	ballMatchRedisMap := make(map[int64]*db_sports2.FootBallMatchRedis, 0)
	result, err := global.GetRds().MGet(matchIds...).Result()
	if err != nil {
		return nil
	}
	for _, val := range result {
		basket := &bbcache.Score{}
		if val != nil {
			err := util.Json2Struct(val.(string), basket)
			if err != nil {
				ballMatchRedisMap[int64(basket.MatchId)] = &db_sports2.FootBallMatchRedis{
					MatchId:     int64(basket.MatchId),
					MatchStatus: basket.MatchStatus,
					HomeScore:   db_sports2.FootBallMatchRedisScore{Score: accumulation(basket.HomeScore)},
					AwayScore:   db_sports2.FootBallMatchRedisScore{Score: accumulation(basket.AwayScore)},
				}
			}
		}
	}
	return ballMatchRedisMap
}

func accumulation(scores []int) int {
	score := 0
	for _, val := range scores {
		score = score + val
	}
	return score
}

/**
* @Author:admin
* @Description: 保存一个联赛菜单到redis中
* @Param :
* @Return :
* @Date: 9:46 2022/6/22
 */

func setRedisCache(list interface{}, key string, expiry int) error {

	cacheData, err := util.Struct2Json(list)
	if err != nil {
		return fmt.Errorf("%s%s", util.Convert2(), err.Error())
	}
	global.GetRds().Set(key, cacheData, time.Duration(expiry)*time.Second)
	return nil
}

/**
* @Author:admin
* @Description:生成redis key
* @Param :
* @Return :
* @Date: 10:02 2022/6/22
 */
func generateCacheKey(key string, type1 int32) string {

	return fmt.Sprintf("%s_%d", key, type1)
}

/**
 * @Author admin
 * @Description //赛程菜单
 * @version: v1.8.0
 * @Date 10:22 2022/7/18
 * @Param
 * @return
 **/
func (s *SportServer) GetCompMenuList(ctx context.Context, req *protobuf.GetMatchListReq) (*protobuf.CompMenuItemResp, error) {
	compMenuItemList := make([]*protobuf.CompMenuItem, 0)
	if list, err := db_sports.GetCompMenuList(compConditionCombine(req)); err == nil {
		//转换赛事首字母
		for _, menu := range list {
			if global.Lang.GetLang() != _const.LangChinese {
				menu.Comp = menu.CompShortEn
				menu.FirstLetter = menu.CompShortEn
				menu.CompShortZh = menu.CompShortEn
			}
			split := strings.Split(strings.TrimLeft(menu.Comp, _const.INCL_EMPTY_STR), _const.EMPTY_STR)
			if len(menu.CompShortZh) > 0 {
				split = strings.Split(strings.TrimLeft(menu.CompShortZh, _const.INCL_EMPTY_STR), _const.EMPTY_STR)
			}
			if len(split) > 0 {
				menu.FirstLetter = util.ZhToLetter(split[0])
			}
		}
		//转换后的赛事列表按字母排序并分组
		if len(list) > 0 {
			slice := groupBySlice(list)
			for _, menuItem := range slice {
				var firstLetter string
				if menuItem[0] != nil {
					firstLetter = menuItem[0].FirstLetter
				}
				compMenuItemList = append(compMenuItemList, &protobuf.CompMenuItem{
					FirstLetter: firstLetter,
					Comps:       menuItem,
				})
			}
		}

	}
	return &protobuf.CompMenuItemResp{CompMenuList: compMenuItemList}, nil
}

/**
 * @Author admin
 * @Description //查询赛事列表
 * @version: v1.8.0
 * @Date 14:14 2022/7/18
 * @Param
 * @return
 **/
func (s *SportServer) GetMatchList(ctx context.Context, req *protobuf.GetMatchListReq) (*protobuf.MatchResp, error) {
	ret := &protobuf.MatchResp{}
	var condition string
	var liveMatchIds string
	if req.IsLive > 0 {
		lives, _ := db_live.GetLivesParam(map[string]interface{}{"matchid": map[string]interface{}{">": 0}}, "", 0, 50)
		if len(lives) == 0 {
			return ret, nil
		}
		var matchIdsArr []string
		for _, v := range lives {
			matchIdsArr = append(matchIdsArr, cast.ToString(v.Matchid))
		}
		liveMatchIds = strings.Join(matchIdsArr, ",")
	}
	condition = matchConditionCombine(req, liveMatchIds)

	list, err := db_sports.GetMatchList(condition, req)
	if err != nil {
		return nil, err
	}
	matchIds := collectMatchIdAndTranLetter(list)
	//主播
	anchorNum := 1
	if req.Terminal == _const.TERMINAL_PC {
		anchorNum = 5
	}
	mergeLive(list, matchIds, anchorNum)
	pbList := &[]*protobuf.Match{}
	err = copier.Copy(&pbList, list)
	if err != nil {
		return nil, err
	}
	ret.MatchList = *pbList
	//合并es结果集
	mergeScore(ret.MatchList, int(req.Size), matchIds)

	//pc 端需要总条数
	if req.Terminal == _const.TERMINAL_PC {
		if cast.ToUint32(req.GetMatchStatus()) == _const.DEF_MATCH_STATUS_FAVORITE {
			ret.Count = int32(db_sports.CountUserFavorite(int(req.GetUserId()), _const.DEF_MATCH_STATUS_FAVORITE))
		} else {
			ret.Count = int32(db_sports.GetMatchListCount(condition))
		}
		if cast.ToUint32(req.MatchStatus) == _const.DEF_MATCH_STATUS_MATCH || cast.ToUint32(req.MatchStatus) == _const.DEF_MATCH_STATUS_DONE || cast.ToUint32(req.MatchStatus) == _const.DEF_MATCH_STATUS_HOT {
			ret.CountOfDays = db_sports.GetMatchListCountOfDays(matchCountConditionCombine(req))
		}
	}
	//取消状态转换
	//transitionStatus(ret)
	return ret, nil
}

/**
 * @Author willi
 * @Description //状态转换
 * @Date 17:48 2022/8/30
 * @Param
 * @return
 **/
func transitionStatus(ret *protobuf.MatchResp) {
	for _, match := range ret.GetMatchList() {
		match.MatchStatus = _const.RawMatchStatusToCustom[match.SportId][match.RawMatchStatus]
		match.MatchDetailStatus = _const.RawMatchStatusToZh[match.SportId][match.RawMatchStatus]
	}
}

/**
 * @Author admin
 * @Description //查询赛事比分列表列表
 * @version: v1.8.0
 * @Date 14:14 2022/7/18
 * @Param
 * @return
 **/
func (s *SportServer) GetMatchScoreList(ctx context.Context, req *protobuf.GetMatchScoreListReq) (*protobuf.MatchScoreResp, error) {
	matchIds := make([]interface{}, 0)
	for _, matchId := range req.GetMatchIds() {
		matchIds = append(matchIds, matchId)
	}
	//先从缓存中取
	redisCacheList := getScoreRedisCache(matchIds)
	//缓存中未有从数据库，并从ES中取比分 并存储到缓存中
	noExistList := getNotExistKey(matchIds, redisCacheList)
	if len(noExistList) > 0 {
		scoreLock.Lock()
		defer scoreLock.Unlock()
		//获取到锁之后再次 从新从redis中获取数据
		redisCacheList = getScoreRedisCache(matchIds)
		noExistList = getNotExistKey(matchIds, redisCacheList)
		//再次获取数据,从Cache中未有完全获取到,从数据库中查找 并缓存
		if len(noExistList) > 0 {
			if matchTimeList, err := db_sports.GetMatchTime(noExistList); err == nil {
				matchEsList := GetScoreList(noExistList, req.SportId)
				dbScoreList := mergeMatchScore(matchTimeList, matchEsList)
				setScoreRedisCache(dbScoreList)
				redisCacheList = append(redisCacheList, dbScoreList...)
			}
		}
		//scoreLock.Unlock()
	}
	return &protobuf.MatchScoreResp{MatchScoreList: redisCacheList}, nil

	//return GetScoreList(matchIds, req.SportId), nil
	//getScoreRedisCache(matchIds)
}
func mergeMatchScore(sourceMatchScore, targetMatchScore []*protobuf.MatchScore) []*protobuf.MatchScore {
	m := make(map[int32]*protobuf.MatchScore)
	for _, val := range targetMatchScore {
		m[val.MatchId] = val
	}
	for _, score := range sourceMatchScore {
		if val, ok := m[score.MatchId]; ok {
			score.FHomeScore = val.FHomeScore //足球主队牌数对象
			score.FAwayScore = val.FAwayScore //足球客队牌数对象
			score.BHomeScore = val.BHomeScore //篮球主队牌数对象
			score.BAwayScore = val.BAwayScore //篮球客队牌数对象
			score.HomeScore = val.HomeScore   //主队当前比分
			score.AwayScore = val.AwayScore   //客队当前比分
		}
	}
	return sourceMatchScore
}

/**
 * @Author willi
 * @Description //TODO
 * @Date 16:43 2022/8/31
 * @Param
 * @return
 **/
func getScoreRedisCache(matchIds []interface{}) []*protobuf.MatchScore {
	arrays := make([]string, len(matchIds))
	for i, v := range matchIds {
		arrays[i] = fmt.Sprintf("matchScoreRedis:%s", cast.ToString(v))
	}
	ballMatchRedis := make([]*protobuf.MatchScore, 0)
	result, _ := global.GetRds().MGet(arrays...).Result()
	for _, val := range result {
		if v, ok := val.(string); ok {
			match := &protobuf.MatchScore{}
			if util.Json2Struct(v, &match) == nil {
				ballMatchRedis = append(ballMatchRedis, match)
			}
		}
	}
	return ballMatchRedis
}

func setScoreRedisCache(matchList []*protobuf.MatchScore) {
	/*var pairs []interface{}
	for _, score := range matchList {
		key := fmt.Sprintf("matchScoreRedis:%s", cast.ToString(score.MatchId))
		if val, err := util.Struct2Json(score); err == nil {
			pairs = append(pairs, key, val)
		}
	}
	models.GetRds().MSet(pairs).Result()*/
	for _, score := range matchList {
		key := fmt.Sprintf("matchScoreRedis:%s", cast.ToString(score.MatchId))
		if val, err := util.Struct2Json(score); err == nil {
			global.GetRds().Set(key, val, 15*time.Second).Err()
		}
	}
}

/**
 * @Author willi
 * @Description //
 * @Date 17:33 2022/8/31
 * @Param
 * @return
 **/
func getNotExistKey(matchIds []interface{}, existKey []*protobuf.MatchScore) []interface{} {
	maps := make(map[int]string, len(matchIds))
	for _, v := range matchIds {
		maps[cast.ToInt(v)] = "0"
	}
	for _, val := range existKey {
		delete(maps, int(val.MatchId))
	}
	arrays := make([]interface{}, 0)
	for key, _ := range maps {
		arrays = append(arrays, key)
	}
	return arrays
}

/**
 * @Author admin
 * @Description 收藏赛事/取消收藏
 * @version: v1.8.0
 * @Date 17:59 2022/7/18
 * @Param
 * @return
 **/
func (s *SportServer) FavoriteMatch(ctx context.Context, req *protobuf.FavoriteReq) (*protobuf.FavoriteResp, error) {
	ret := &protobuf.FavoriteResp{Code: _const.RPC_FAILED}
	switch req.Operate {
	case _const.FAVORITE_SUBSCRIBE:
		ret = subscribeFavorite(req)
	case _const.FAVORITE_CANCEL:
		ret = cancelFavorite(req)
	}
	ret.FavoriteQuantity = int32(db_sports.CountUserFavorite(int(req.UserId), int(req.SportId)))
	ret.Quantity = int32(db_sports.GetFavoriteQuantityByMatch(int(req.MatchId)))
	return ret, nil
}

/**
 * @Author admin
 * @Description //会员收藏数量
 * @version: v1.8.0
 * @Date 11:20 2022/7/22
 * @Param
 * @return
 **/
func (s *SportServer) CountUserFavorite(ctx context.Context, req *protobuf.CountUserFavoriteReq) (*protobuf.UserFavoriteResp, error) {
	ret := &protobuf.UserFavoriteResp{Quantity: 0}
	ret.Quantity = int32(db_sports.CountUserFavorite(int(req.UserId), int(req.SportId)))
	return ret, nil
}

/**
 * @Author admin
 * @Description //根据赛事Id查询赛事
 * @version: v1.8.0
 * @Date 14:06 2022/7/30
 * @Param
 * @return
 **/
func (s *SportServer) GetMatchById(ctx context.Context, req *protobuf.GetMatchByIdReq) (*protobuf.Match, error) {
	var match *api_models.EsMatchTempSchedule
	if global.Lang.GetLang() == _const.LangChinese {
		match, _ = db_sports.GetEsIdByInfo(int(req.GetMatchId()), int(req.SportId))
	} else {
		match, _ = db_sports.GetEsIdByInfo(int(req.GetMatchId()), int(req.SportId))
	}
	//global.Log.Debugf("Es 比赛数据 match:%+v", match)
	pbMatch := &protobuf.Match{}
	if match != nil {
		pbMatch.Away = match.Away
		pbMatch.AwayLogo = match.AwayLogo
		pbMatch.AwayScore = int32(match.AwayScore)
		pbMatch.Comp = match.Comp
		pbMatch.CompShortZh = match.CompShortZh
		pbMatch.Home = match.Home
		pbMatch.HomeLogo = match.HomeLogo
		pbMatch.HomeScore = int32(match.HomeScore)
		pbMatch.MatchDetailStatus = match.MatchDetailStatus
		pbMatch.MatchId = int32(match.MatchId)
		pbMatch.MatchStatus = int32(match.MatchStatus)
		pbMatch.MatchTime = match.MatchTime
		pbMatch.RawMatchStatus = int32(match.RawMatchStatus)
		pbMatch.SportId = int32(match.SportId)
		pbMatch.TimePlayed = int32(match.TimePlayed)
		if global.Lang.GetLang() != _const.LangChinese {
			pbMatch.Comp = match.CompShortEn
			pbMatch.CompShortZh = match.CompShortEn
			pbMatch.Home = match.HomeEn
			pbMatch.Away = match.AwayEn
			pbMatch.MatchDetailStatus = util.GetMatchStatusCodeEn(match.SportId, match.RawMatchStatus)
		}
		mergeScore([]*protobuf.Match{pbMatch}, 1, []interface{}{match.MatchId})
		split := strings.Split(strings.TrimLeft(match.Comp, _const.INCL_EMPTY_STR), _const.EMPTY_STR)
		if len(split) > 0 {
			if len(match.CompShortZh) > 0 {
				split = strings.Split(strings.TrimLeft(match.CompShortZh, _const.INCL_EMPTY_STR), _const.EMPTY_STR)
			}
			pbMatch.FirstLetter = util.ZhToLetter(split[0])
		}
	} else {
		return nil, errors.New("match is nul")
	}
	return pbMatch, nil
}

/**
 * @Author admin
 * @Description //赛事收藏
 * @version: v1.8.0
 * @Date 16:10 2022/7/18
 * @Param
 * @return
 **/
func subscribeFavorite(req *protobuf.FavoriteReq) *protobuf.FavoriteResp {
	ret := &protobuf.FavoriteResp{Code: _const.RPC_FAILED}
	if match, err := db_sports.CountMatch(req.MatchId, req.SportId); err == nil {
		matchFavorite := generateMatchFavorite(req, match)
		ret.Code = commitSubscribeFavorite(matchFavorite, req)
	}

	//imsdk.GetSdk().AddTags([]string{strconv.Itoa(int(req.SenderUid))}, []string{strconv.Itoa(int(req.MatchId))})
	return ret
}

/**
 * @Author admin
 * @Description //生成一个赛事收藏Model
 * @version: v1.8.0
 * @Date 16:16 2022/7/20
 * @Param
 * @return
 **/
func generateMatchFavorite(req *protobuf.FavoriteReq, match *db_sports2.MatchTempSchedule) *db_sports2.MatchFavorite {
	return &db_sports2.MatchFavorite{
		UserId:     int(req.UserId),
		MatchId:    int(req.MatchId),
		SportId:    match.SportId,
		CreateTime: time.Now().Unix(),
	}
}

/**
 * @Author admin
 * @Description //事务提交代码
 * @version: v1.8.0
 * @Date 16:13 2022/7/20
 * @Param
 * @return
 **/
func commitSubscribeFavorite(matchFavorite *db_sports2.MatchFavorite, req *protobuf.FavoriteReq) int32 {
	session := global.Sport().Begin()
	if row := db_sports.InsertFavorite(session, matchFavorite); row == 0 {
		session.Rollback()
		return _const.RPC_FAILED
	}
	if row := db_sports.FavoriteStatistics(session, int(req.MatchId), 1); row == 0 {
		session.Rollback()
		return _const.RPC_FAILED
	}
	if err := session.Commit().Error; err != nil {
		session.Rollback()
		return _const.RPC_FAILED
	}
	return _const.RPC_SUCCEED
}

/**
 * @Author admin
 * @Description //赛事收藏取消
 * @version: v1.8.0
 * @Date 16:11 2022/7/18
 * @Param
 * @return
 **/
func cancelFavorite(req *protobuf.FavoriteReq) *protobuf.FavoriteResp {
	ret := &protobuf.FavoriteResp{}
	session := global.Sport().Begin()
	if row := db_sports.DelFavorite(session, req); row == 0 {
		session.Rollback()
		ret.Code = _const.RPC_FAILED
		return ret
	}
	if row := db_sports.FavoriteStatistics(session, int(req.MatchId), -1); row == 0 {
		session.Rollback()
		ret.Code = _const.RPC_FAILED
		return ret
	}
	if err := session.Commit().Error; err != nil {
		session.Rollback()
		ret.Code = _const.RPC_FAILED
		return ret
	}

	imsdk.GetSdk().DeleteTags([]string{strconv.Itoa(int(req.UserId))}, []string{strconv.Itoa(int(req.MatchId))})

	ret.Code = _const.RPC_SUCCEED
	return ret
}

/**
 * @Author admin
 * @Description //合并对象的比分
 * @version: v1.8.0
 * @Date 14:37 2022/7/18
 * @Param
 * @return
 **/
func mergeScore(matchList []*protobuf.Match, size int, matchIds []interface{}) {
	if len(matchIds) > 0 {
		var footMatchIds []interface{}
		var basketMatchIds []interface{}
		var cricketMatchIds []interface{}

		for _, v := range matchList {
			if v.SportId == _const.FOOT_BALL_SPORT_ID {
				footMatchIds = append(footMatchIds, v.MatchId)
			} else if v.SportId == _const.BASKET_BALL_SPORT_ID {
				basketMatchIds = append(basketMatchIds, v.MatchId)
			} else if v.SportId == _const.CRICKET_BALL_SPORT_ID {
				cricketMatchIds = append(cricketMatchIds, v.MatchId)
			}
		}
		videoLiveUrls, _ := cache.GetAllVideoLiveUrl(global.Sport())
		if len(footMatchIds) > 0 {
			footScores := db_sports.EsFootballScore(footMatchIds, size)
			footMap := footBallScoreListToMap(footScores)
			for _, match := range matchList {
				isVideoLiveUrl := int32(0)
				if _, ok := videoLiveUrls[int(match.MatchId)]; ok {
					isVideoLiveUrl = 1
				}
				if val, ok := footMap[int(match.MatchId)]; ok {
					match.FHomeScore = &protobuf.FootScore{
						Score:        int32(val.HomeScore.Score),
						HalfScore:    int32(val.HomeScore.HalfScore),
						RedCard:      int32(val.HomeScore.RedCard),
						YellowCard:   int32(val.HomeScore.YellowCard),
						CornerKick:   int32(val.HomeScore.CornerKick),
						OtScore:      int32(val.HomeScore.OTScore),
						PenaltyScore: int32(val.HomeScore.PenaltyScore),
					}
					match.FAwayScore = &protobuf.FootScore{
						Score:        int32(val.AwayScore.Score),
						HalfScore:    int32(val.AwayScore.HalfScore),
						RedCard:      int32(val.AwayScore.RedCard),
						YellowCard:   int32(val.AwayScore.YellowCard),
						CornerKick:   int32(val.AwayScore.CornerKick),
						OtScore:      int32(val.AwayScore.OTScore),
						PenaltyScore: int32(val.AwayScore.PenaltyScore),
					}
					match.AwayScore = int32(val.AwayScore.Score)
					match.HomeScore = int32(val.HomeScore.Score)
					if match.MatchStatus == db_sports.MatchStaring {
						match.HasVideoLink = isVideoLiveUrl
					}
				}
			}
		}

		if len(basketMatchIds) > 0 {
			//case util.BASKET_BALL_SPORT_ID:
			basketScores := db_sports.EsBasketballScore(basketMatchIds, size)
			basketMap := basketBallListToMap(basketScores)
			for _, match := range matchList {
				isVideoLiveUrl := int32(0)
				if _, ok := videoLiveUrls[int(match.MatchId)]; ok {
					isVideoLiveUrl = 1
				}
				if val, ok := basketMap[int(match.MatchId)]; ok {
					toInt32, totalVal := util.SliceIntToInt32(val.HomeScore)
					match.BHomeScore = toInt32
					match.HomeScore = int32(totalVal)
					toInt32, totalVal = util.SliceIntToInt32(val.AwayScore)
					match.BAwayScore = toInt32
					match.AwayScore = int32(totalVal)
					if match.MatchStatus == db_sports.MatchStaring {
						match.HasVideoLink = isVideoLiveUrl
					}
				}
			}
		}

		if len(cricketMatchIds) > 0 {
			for _, match := range matchList {
				isVideoLiveUrl := int32(0)
				if _, ok := videoLiveUrls[int(match.MatchId)]; ok {
					isVideoLiveUrl = 1
				}
				if match.MatchStatus == db_sports.MatchStaring {
					match.HasVideoLink = isVideoLiveUrl
				}
			}
		}
	}

	//}
}

/**
 * @Author admin
 * @Description //合并对象的比分
 * @version: v1.8.0
 * @Date 14:37 2022/7/18
 * @Param
 * @return
 **/
func GetScoreList(matchIds []interface{}, sportId uint32) []*protobuf.MatchScore {
	//resp := &pb.MatchScoreResp{}
	scoreList := make([]*protobuf.MatchScore, 0)
	if len(matchIds) > 0 {
		//switch sportId {
		//case util.FOOT_BALL_SPORT_ID:
		footScores := db_sports.EsFootballScore(matchIds, len(matchIds))
		for _, score := range footScores {
			matchScore := &protobuf.MatchScore{
				FHomeScore: &protobuf.FootScore{
					Score:        int32(score.HomeScore.Score),
					HalfScore:    int32(score.HomeScore.HalfScore),
					RedCard:      int32(score.HomeScore.RedCard),
					YellowCard:   int32(score.HomeScore.YellowCard),
					CornerKick:   int32(score.HomeScore.CornerKick),
					OtScore:      int32(score.HomeScore.OTScore),
					PenaltyScore: int32(score.HomeScore.PenaltyScore),
				},
				FAwayScore: &protobuf.FootScore{
					Score:        int32(score.AwayScore.Score),
					HalfScore:    int32(score.AwayScore.HalfScore),
					RedCard:      int32(score.AwayScore.RedCard),
					YellowCard:   int32(score.AwayScore.YellowCard),
					CornerKick:   int32(score.AwayScore.CornerKick),
					OtScore:      int32(score.AwayScore.OTScore),
					PenaltyScore: int32(score.AwayScore.PenaltyScore),
				},
				MatchId:   int32(score.Id),
				AwayScore: int32(score.AwayScore.Score),
				HomeScore: int32(score.HomeScore.Score),
			}
			scoreList = append(scoreList, matchScore)
		}
		//case util.BASKET_BALL_SPORT_ID:
		basketScores := db_sports.EsBasketballScore(matchIds, len(matchIds))
		for _, score := range basketScores {
			toInt32, totalVal := util.SliceIntToInt32(score.HomeScore)
			aToInt32, aTotalVal := util.SliceIntToInt32(score.AwayScore)
			matchScore := &protobuf.MatchScore{
				BHomeScore: toInt32,
				HomeScore:  int32(totalVal),
				MatchId:    int32(score.MatchId),
				BAwayScore: aToInt32,
				AwayScore:  int32(aTotalVal),
			}
			scoreList = append(scoreList, matchScore)
		}
		//}
	}
	return scoreList
}

/**
 * @Author admin   来自新的版本
 * @Description //
 * @version: v1.8.0
 * @Date 15:30 2022/7/20
 * @Param
 * @return
 **/
func mergeLive(matchList []*db_sports2.Match, matchIds []interface{}, anchorNum int) {
	if len(matchIds) > 0 {
		lives := db_sports.GetMatchLive(matchIds, anchorNum)
		toMap := liveListToMap(lives)
		for _, match := range matchList {
			if val, ok := toMap[int(match.MatchId)]; ok && match.MatchStatus != _const.DEF_MATCH_STATUS_DONE {
				match.HaveReservation = 1
				match.AnchorAvatar = val.AnchorAvatars
				match.AnchorId = val.AnchorIds
			}
			if global.Lang.GetLang() != _const.LangChinese {
				match.MatchDetailStatus = util.GetMatchStatusCodeEn(int(match.SportId), uint32(match.RawMatchStatus))
				match.Away = match.AwayEn
				match.Home = match.HomeEn
				match.Comp = match.CompShortEn
				match.CompShortZh = match.CompShortEn
				global.Log.Debugf("赛程id:%v,comp:%v", match.MatchId, match.Comp)
			}
		}
	}
}

/**
 * @Author admin
 * @Description //list 转Map
 * @version: v1.8.0
 * @Date 15:30 2022/7/20
 * @Param
 * @return
 **/
func liveListToMap(list []*api_models.GetAnchorReservations) map[int]*api_models.GetAnchorReservations {
	m := make(map[int]*api_models.GetAnchorReservations)
	for _, val := range list {
		m[int(val.MatchId)] = val
	}
	return m
}

/**
 * @Author admin
 * @Description //足球List 转Map
 * @version: v1.8.0
 * @Date 15:30 2022/7/20
 * @Param
 * @return
 **/
func footBallScoreListToMap(list []*db_sports2.FootballScores) map[int]*db_sports2.FootballScores {
	m := make(map[int]*db_sports2.FootballScores)
	for _, val := range list {
		m[val.Id] = val
	}
	return m
}

/**
 * @Author admin
 * @Description //篮球List 转map
 * @version: v1.8.0
 * @Date 15:31 2022/7/20
 * @Param
 * @return
 **/
func basketBallListToMap(list []*db_sports2.BasketballScore) map[int]*db_sports2.BasketballScore {
	m := make(map[int]*db_sports2.BasketballScore)
	for _, val := range list {
		m[val.MatchId] = val
	}
	return m
}

/**
 * @Author admin
 * @Description //收集正在进行中或已完赛的比赛ID
 * @version: v1.8.0
 * @Date 14:35 2022/7/18
 * @Param
 * @return
 **/
func collectMatchIdAndTranLetter(matchList []*db_sports2.Match) []interface{} {
	matchIds := make([]interface{}, 0)
	for _, match := range matchList {
		if match == nil {
			continue
		}
		//转换首字母
		split := strings.Split(strings.TrimLeft(match.Comp, _const.INCL_EMPTY_STR), _const.EMPTY_STR)
		if len(match.CompShortZh) > 0 {
			split = strings.Split(strings.TrimLeft(match.CompShortZh, _const.INCL_EMPTY_STR), _const.EMPTY_STR)
		}
		if len(split) > 0 {
			match.FirstLetter = util.ZhToLetter(split[0])
		}
		//if match.MatchStatus == util.DEF_MATCH_STATUS_ACTION || match.MatchStatus == util.DEF_MATCH_STATUS_DONE {
		matchIds = append(matchIds, match.MatchId)
		//}
	}
	return matchIds
}

/**
 * @Author admin
 * @Description //比赛sql条件组件
 * @version: v1.8.0
 * @Date 9:15 2022/7/18
 * @Param
 * @return
 **/
func compConditionCombine(req *protobuf.GetMatchListReq) string {
	sql := _const.EMPTY_STR
	begin, end := time2.INaDay2Unix(req.Date)
	switch req.MatchStatus {
	case _const.DEF_MATCH_STATUS_ACTION:
		//进行中会查询昨天到今天状态为1
		begin, end = time2.YesterdayAndToday()
	case _const.DEF_MATCH_STATUS_ALL:
		//默认展示当日0AM点到次日12AM
		begin, end = time2.TodayAndTomorrow()
		/*	case util.DEF_MATCH_SEARCH:
			begin = util.DateSkewing(-7)
			end = util.DateSkewingEnd(7)*/
	default:
		begin, end = time2.INaDay2Unix(req.Date)
	}
	sql = fmt.Sprintf("%s match_time>=%d and match_time<=%d and", sql, begin, end)
	switch req.MatchStatus {
	case _const.DEF_MATCH_STATUS_ACTION, _const.DEF_MATCH_STATUS_DONE:
		sql = fmt.Sprintf("%s match_status = %d and", sql, req.MatchStatus)
	case _const.DEF_MATCH_STATUS_MATCH:
		sql = fmt.Sprintf("%s match_status in(2,4) and", sql)
	case _const.DEF_MATCH_STATUS_HOT:
		sql = fmt.Sprintf("%s flag = %d and", sql, _const.MATCH_IMPT)
	}
	if req.SportId > 0 && req.SportId != 999 {
		sql = fmt.Sprintf("%s sport_id=%d and", sql, req.SportId)
	} else if req.SportId == 999 {
		// sql = fmt.Sprintf("%s t1.flag=2 and", sql)
	}

	return strings.TrimRight(sql, _const.SQL_KEYWORK_AND)
}

/*
*
  - @Author admin
  - @Description //比赛sql条件组件  足球 0:"比赛异常",1:"未开赛",2:"上半场",3:"中场",4:"下半场",5:"加时赛",6:"加时赛(弃用)",7:"点球决战",8:"完场",9:"推迟",10:"中断",11:"腰斩",12:"取消",13:"待定",
    //0:"比赛异常",1:"未开赛",2:"第一节",3:"第一节完",4:"第二节",5:"第二节完",6:"第三节",7:"第三节完",8:"第四节",9:"加时",10:"完场",11:"中断",12:"取消",13:"延期",14:"腰斩",15:"待定",
  - @version: v1.8.0
  - @Date 9:15 2022/7/18
  - @Param
  - @return
    *
*/
func matchConditionCombine(req *protobuf.GetMatchListReq, liveMatchIds string) string {
	sql := _const.EMPTY_STR
	var begin, end int64
	//先处理时间
	switch req.MatchStatus {
	case _const.DEF_MATCH_STATUS_ACTION:
		//进行中会查询昨天到今天状态为1
		begin, end = time2.YesterdayAndToday()
	case _const.DEF_MATCH_STATUS_ALL:
		//默认展示当日0AM点到23:59:59
		begin, end = time2.TodayAndTomorrow()
	case _const.DEF_MATCH_SEARCH:
		begin = time2.DateSkewing(-7)
		end = time2.DateSkewingEnd(7)
	default:
		begin, end = time2.INaDay2Unix(req.Date)
	}
	if req.MatchStatus != _const.DEF_MATCH_STATUS_FAVORITE {
		sql = fmt.Sprintf("%s match_time>=%d and match_time<=%d and", sql, begin, end)
	}
	if req.SportId > 0 && req.SportId != 999 {
		sql = fmt.Sprintf("%s t1.sport_id=%d and", sql, req.SportId)
	} else if req.SportId == 999 {
		// sql = fmt.Sprintf("%s t1.flag=2 and", sql)
	}
	coms := _const.EMPTY_STR
	if len(req.Comps) > 0 {
		for _, comp := range req.Comps {
			coms = fmt.Sprintf("%s,'%s'", coms, comp)
		}
		coms = strings.TrimLeft(coms, ",")
		if global.Lang.GetLang() == _const.LangChinese {
			sql = fmt.Sprintf("%s comp in (%s) and", sql, coms)
		} else {
			sql = fmt.Sprintf("%s comp_short_en in (%s) and", sql, coms)
		}

	}

	switch req.MatchStatus {
	case _const.DEF_MATCH_STATUS_ACTION, _const.DEF_MATCH_STATUS_DONE:
		sql = fmt.Sprintf("%s match_status = %d and", sql, req.MatchStatus)
	case _const.DEF_MATCH_STATUS_MATCH:
		sql = fmt.Sprintf("%s match_status in(2,4)  and", sql)
	case _const.DEF_MATCH_STATUS_HOT:
		sql = fmt.Sprintf("%s flag = %d and", sql, _const.MATCH_IMPT)
	case _const.DEF_MATCH_STATUS_FAVORITE:
		if req.SportId != 999 {
			sql = fmt.Sprintf("t2.user_id=%d and t2.sport_id=%d", req.UserId, req.SportId)
		} else {
			sql = fmt.Sprintf("t2.user_id=%d", req.UserId)
		}
	case _const.DEF_MATCH_SEARCH:
		matchName := strings.ToUpper(req.MatchName)
		if global.Lang.GetLang() == _const.LangChinese {
			sql = fmt.Sprintf("%s (UPPER(comp_short_zh) like '%%%s%%' or UPPER(home) like '%%%s%%' or UPPER(away) like '%%%s%%')", sql, matchName, matchName, matchName)
		} else {
			sql = fmt.Sprintf("%s (UPPER(comp_short_en) like '%%%s%%' or UPPER(home_en) like '%%%s%%' or UPPER(away_en) like '%%%s%%')", sql, matchName, matchName, matchName)
		}

	}
	if req.IsLive > 0 {
		sql = fmt.Sprintf("%s t1.match_id in(%s) and", sql, liveMatchIds)
	}
	sql = strings.TrimRight(sql, _const.SQL_KEYWORK_AND)
	switch req.MatchStatus {
	case _const.DEF_MATCH_STATUS_DONE:
		sql = fmt.Sprintf("%s order by t1.match_time desc", sql)
	case _const.DEF_MATCH_SEARCH:
		sql = fmt.Sprintf("%s order by t1.match_time desc", sql)
	default:
		sql = fmt.Sprintf("%s order by t1.match_status,match_time ", sql)
	}
	return sql
}

func matchCountConditionCombine(req *protobuf.GetMatchListReq) string {
	sql := _const.EMPTY_STR
	var begin, end int64
	begin = time2.DateSkewing(-8)
	end = time2.DateSkewingEnd(8)
	//先处理时间

	sql = fmt.Sprintf("%s match_time>=%d and match_time<=%d and", sql, begin, end)

	if req.SportId > 0 && req.SportId != 999 {
		sql = fmt.Sprintf("%s t1.sport_id=%d and", sql, req.SportId)
	} else if req.SportId == 999 {
		// sql = fmt.Sprintf("%s t1.flag=2 and", sql)
	}
	coms := _const.EMPTY_STR
	if len(req.Comps) > 0 {
		for _, comp := range req.Comps {
			coms = fmt.Sprintf("%s,'%s'", coms, comp)
		}
		coms = strings.TrimLeft(coms, ",")
		sql = fmt.Sprintf("%s comp in (%s) and", sql, coms)
	}

	switch req.MatchStatus {
	case _const.DEF_MATCH_STATUS_DONE:
		sql = fmt.Sprintf("%s match_status = %d and", sql, req.MatchStatus)
	case _const.DEF_MATCH_STATUS_MATCH:
		sql = fmt.Sprintf("%s match_status in(2,4)  and", sql)
	case _const.DEF_MATCH_STATUS_HOT:
		sql = fmt.Sprintf("%s flag = %d", sql, 2)
	}
	if req.IsLive > 0 {
		sql = fmt.Sprintf("%s t1.match_id in(select DISTINCT matchid match_id from db.db_live) and", sql)
	}
	sql = strings.TrimRight(sql, _const.SQL_KEYWORK_AND)
	return sql
}

type groupByLetter []*protobuf.CompMenu

func (s groupByLetter) Len() int           { return len(s) }
func (s groupByLetter) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s groupByLetter) Less(i, j int) bool { return s[i].FirstLetter < s[j].FirstLetter }

/**
 * @Author admin
 * @Description //按赛事首字母排序
 * @version: v1.8.0
 * @Date 9:08 2022/7/18
 * @Param
 * @return
 **/
func groupBySlice(list []*protobuf.CompMenu) [][]*protobuf.CompMenu {
	sort.Sort(groupByLetter(list))
	compList := make([][]*protobuf.CompMenu, 0)
	i := 0
	var j int
	for {
		if i >= len(list) {
			break
		}
		for j = i + 1; j < len(list) && list[i].FirstLetter == list[j].FirstLetter; j++ {
		}
		compList = append(compList, list[i:j])
		i = j
	}
	return compList
}
